package cn.metona.utils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;

public final class MapUtils {

    // 私有构造函数，防止实例化
    private MapUtils() {
        throw new UnsupportedOperationException("Utility class");
    }

    /**
     * 合并两个 Map，如果键冲突，使用提供的合并函数解决冲突
     *
     * @param map1    第一个 Map
     * @param map2    第二个 Map
     * @param merger  合并函数，用于解决键冲突
     * @param <K>     键类型
     * @param <V>     值类型
     * @return 合并后的 Map
     */
    public static <K, V> Map<K, V> merge(Map<K, V> map1, Map<K, V> map2, BiFunction<V, V, V> merger) {
        Map<K, V> result = new HashMap<>(map1);
        map2.forEach((key, value) -> result.merge(key, value, merger));
        return result;
    }

    /**
     * 过滤 Map，保留满足条件的键值对
     *
     * @param map       要过滤的 Map
     * @param predicate 过滤条件
     * @param <K>       键类型
     * @param <V>       值类型
     * @return 过滤后的 Map
     */
    public static <K, V> Map<K, V> filter(Map<K, V> map, Predicate<Map.Entry<K, V>> predicate) {
        return map.entrySet().stream()
                .filter(predicate)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }

    /**
     * 将 Map 的键和值进行转换
     *
     * @param map          要转换的 Map
     * @param keyMapper    键的转换函数
     * @param valueMapper  值的转换函数
     * @param <K1>         原始键类型
     * @param <V1>         原始值类型
     * @param <K2>         转换后的键类型
     * @param <V2>         转换后的值类型
     * @return 转换后的 Map
     */
    public static <K1, V1, K2, V2> Map<K2, V2> transform(Map<K1, V1> map,
                                                         Function<K1, K2> keyMapper,
                                                         Function<V1, V2> valueMapper) {
        return map.entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> keyMapper.apply(entry.getKey()),
                        entry -> valueMapper.apply(entry.getValue())
                ));
    }

    /**
     * 将 Map 转换为排序后的 Map
     *
     * @param map       要排序的 Map
     * @param comparator 排序比较器
     * @param <K>       键类型
     * @param <V>       值类型
     * @return 排序后的 Map
     */
    public static <K, V> Map<K, V> sort(Map<K, V> map, Comparator<Map.Entry<K, V>> comparator) {
        return map.entrySet().stream()
                .sorted(comparator)
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (oldValue, newValue) -> oldValue,
                        LinkedHashMap::new
                ));
    }

    /**
     * 检查 Map 是否为空或 null
     *
     * @param map 要检查的 Map
     * @return 如果 Map 为 null 或空，返回 true；否则返回 false
     */
    public static boolean isEmpty(Map<?, ?> map) {
        return map == null || map.isEmpty();
    }

    /**
     * 检查 Map 是否不为空且不为 null
     *
     * @param map 要检查的 Map
     * @return 如果 Map 不为 null 且不为空，返回 true；否则返回 false
     */
    public static boolean isNotEmpty(Map<?, ?> map) {
        return !isEmpty(map);
    }

    /**
     * 反转 Map 的键和值
     *
     * @param map 要反转的 Map
     * @param <K> 键类型
     * @param <V> 值类型
     * @return 反转后的 Map
     * @throws IllegalStateException 如果 Map 中存在重复值
     */
    public static <K, V> Map<V, K> invert(Map<K, V> map) {
        return map.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
    }

    /**
     * 获取 Map 中指定键的值，如果键不存在则返回默认值
     *
     * @param map          要查询的 Map
     * @param key          键
     * @param defaultValue 默认值
     * @param <K>          键类型
     * @param <V>          值类型
     * @return 键对应的值，或默认值
     */
    public static <K, V> V getOrDefault(Map<K, V> map, K key, V defaultValue) {
        return map.getOrDefault(key, defaultValue);
    }

    /**
     * 将 Map 转换为字符串表示
     *
     * @param map 要转换的 Map
     * @return Map 的字符串表示
     */
    public static String toString(Map<?, ?> map) {
        if (isEmpty(map)) {
            return "{}";
        }
        return map.entrySet().stream()
                .map(entry -> entry.getKey() + "=" + entry.getValue())
                .collect(Collectors.joining(", ", "{", "}"));
    }

    /**
     * 将 Map 的值转换为列表
     *
     * @param map 要转换的 Map
     * @param <K> 键类型
     * @param <V> 值类型
     * @return 值的列表
     */
    public static <K, V> List<V> valuesToList(Map<K, V> map) {
        return new ArrayList<>(map.values());
    }

    /**
     * 将 Map 的键转换为列表
     *
     * @param map 要转换的 Map
     * @param <K> 键类型
     * @param <V> 值类型
     * @return 键的列表
     */
    public static <K, V> List<K> keysToList(Map<K, V> map) {
        return new ArrayList<>(map.keySet());
    }

    /**
     * 将 Map 的键值对转换为自定义对象的列表
     *
     * @param map          要转换的 Map
     * @param entryMapper  转换函数
     * @param <K>          键类型
     * @param <V>          值类型
     * @param <T>          目标对象类型
     * @return 自定义对象的列表
     */
    public static <K, V, T> List<T> mapToList(Map<K, V> map, Function<Map.Entry<K, V>, T> entryMapper) {
        return map.entrySet().stream()
                .map(entryMapper)
                .collect(Collectors.toList());
    }

    /**
     * 计算 Map 中值的总和（适用于数值类型的值）
     *
     * @param map 要计算的 Map
     * @param <K> 键类型
     * @return 值的总和
     */
    public static <K> double sumValues(Map<K, ? extends Number> map) {
        return map.values().stream()
                .mapToDouble(Number::doubleValue)
                .sum();
    }

    /**
     * 将 Map 的值转换为 Set
     *
     * @param map 要转换的 Map
     * @param <K> 键类型
     * @param <V> 值类型
     * @return 值的 Set
     */
    public static <K, V> Set<V> valuesToSet(Map<K, V> map) {
        return new HashSet<>(map.values());
    }

    /**
     * 将 Map 的键转换为 Set
     *
     * @param map 要转换的 Map
     * @param <K> 键类型
     * @param <V> 值类型
     * @return 键的 Set
     */
    public static <K, V> Set<K> keysToSet(Map<K, V> map) {
        return new HashSet<>(map.keySet());
    }

    /**
     * 将 Map 转换为 Properties 对象
     *
     * @param map 要转换的 Map
     * @return Properties 对象
     */
    public static Properties toProperties(Map<String, String> map) {
        Properties properties = new Properties();
        properties.putAll(map);
        return properties;
    }

    /**
     * 将 Map 分组为子 Map
     *
     * @param map          要分组的 Map
     * @param groupByKey   分组函数
     * @param <K>          原始键类型
     * @param <V>          值类型
     * @param <G>          分组键类型
     * @return 分组后的 Map
     */
    public static <K, V, G> Map<G, Map<K, V>> groupBy(Map<K, V> map, Function<K, G> groupByKey) {
        return map.entrySet().stream()
                .collect(Collectors.groupingBy(
                        entry -> groupByKey.apply(entry.getKey()),
                        Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)
                ));
    }

    /**
     * 将 Map 的值进行累加（适用于数值类型的值）
     *
     * @param map 要累加的 Map
     * @param <K> 键类型
     * @return 累加后的 Map
     */
    public static <K> Map<K, Double> accumulateValues(Map<K, ? extends Number> map) {
        Map<K, Double> result = new HashMap<>();
        map.forEach((key, value) -> result.merge(key, value.doubleValue(), Double::sum));
        return result;
    }

    /**
     * 将 Map 的键值对转换为另一个 Map
     *
     * @param map          要转换的 Map
     * @param keyMapper    键的转换函数
     * @param valueMapper  值的转换函数
     * @param <K1>         原始键类型
     * @param <V1>         原始值类型
     * @param <K2>         转换后的键类型
     * @param <V2>         转换后的值类型
     * @return 转换后的 Map
     */
    public static <K1, V1, K2, V2> Map<K2, V2> mapEntries(Map<K1, V1> map,
                                                          Function<K1, K2> keyMapper,
                                                          Function<V1, V2> valueMapper) {
        return map.entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> keyMapper.apply(entry.getKey()),
                        entry -> valueMapper.apply(entry.getValue())
                ));
    }

    /**
     * 将 Map 的键值对转换为另一个 Map，并处理键冲突
     *
     * @param map          要转换的 Map
     * @param keyMapper    键的转换函数
     * @param valueMapper  值的转换函数
     * @param mergeFunction 键冲突时的合并函数
     * @param <K1>         原始键类型
     * @param <V1>         原始值类型
     * @param <K2>         转换后的键类型
     * @param <V2>         转换后的值类型
     * @return 转换后的 Map
     */
    public static <K1, V1, K2, V2> Map<K2, V2> mapEntries(Map<K1, V1> map,
                                                          Function<K1, K2> keyMapper,
                                                          Function<V1, V2> valueMapper,
                                                          BinaryOperator<V2> mergeFunction) {
        return map.entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> keyMapper.apply(entry.getKey()),
                        entry -> valueMapper.apply(entry.getValue()),
                        mergeFunction
                ));
    }

    /**
     * 将 Map 的键值对转换为另一个 Map，并处理键冲突，同时指定 Map 的实现类
     *
     * @param map          要转换的 Map
     * @param keyMapper    键的转换函数
     * @param valueMapper  值的转换函数
     * @param mergeFunction 键冲突时的合并函数
     * @param mapFactory    Map 的工厂方法
     * @param <K1>         原始键类型
     * @param <V1>         原始值类型
     * @param <K2>         转换后的键类型
     * @param <V2>         转换后的值类型
     * @param <M>          目标 Map 类型
     * @return 转换后的 Map
     */
    public static <K1, V1, K2, V2, M extends Map<K2, V2>> M mapEntries(Map<K1, V1> map,
                                                                       Function<K1, K2> keyMapper,
                                                                       Function<V1, V2> valueMapper,
                                                                       BinaryOperator<V2> mergeFunction,
                                                                       Supplier<M> mapFactory) {
        return map.entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> keyMapper.apply(entry.getKey()),
                        entry -> valueMapper.apply(entry.getValue()),
                        mergeFunction,
                        mapFactory
                ));
    }

    /**
     * 将 Map 的键值对转换为另一个 Map，并处理键冲突，同时指定 Map 的实现类
     *
     * @param map          要转换的 Map
     * @param keyMapper    键的转换函数
     * @param valueMapper  值的转换函数
     * @param mapFactory    Map 的工厂方法
     * @param <K1>         原始键类型
     * @param <V1>         原始值类型
     * @param <K2>         转换后的键类型
     * @param <V2>         转换后的值类型
     * @param <M>          目标 Map 类型
     * @return 转换后的 Map
     */
    public static <K1, V1, K2, V2, M extends Map<K2, V2>> M mapEntries(Map<K1, V1> map,
                                                                       Function<K1, K2> keyMapper,
                                                                       Function<V1, V2> valueMapper,
                                                                       Supplier<M> mapFactory) {
        return map.entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> keyMapper.apply(entry.getKey()),
                        entry -> valueMapper.apply(entry.getValue()),
                        (v1, v2) -> v2, // 默认使用后者覆盖前者
                        mapFactory
                ));
    }

    /**
     * 从 Map 中获取 String 类型的值
     *
     * @param map          Map
     * @param key          键
     * @param defaultValue 默认值
     * @return 值或默认值
     */
    public static String getString(Map<?, ?> map, Object key, String defaultValue) {
        Objects.requireNonNull(map, "Map must not be null");
        Object value = map.get(key);
        return value != null ? value.toString() : defaultValue;
    }

    /**
     * 从 Map 中获取 Integer 类型的值
     *
     * @param map          Map
     * @param key          键
     * @param defaultValue 默认值
     * @return 值或默认值
     */
    public static Integer getInteger(Map<?, ?> map, Object key, Integer defaultValue) {
        Objects.requireNonNull(map, "Map must not be null");
        Object value = map.get(key);
        if (value instanceof Number) {
            return ((Number) value).intValue();
        } else if (value instanceof String) {
            try {
                return Integer.parseInt((String) value);
            } catch (NumberFormatException e) {
                return defaultValue;
            }
        }
        return defaultValue;
    }

    /**
     * 从 Map 中获取 Long 类型的值
     *
     * @param map          Map
     * @param key          键
     * @param defaultValue 默认值
     * @return 值或默认值
     */
    public static Long getLong(Map<?, ?> map, Object key, Long defaultValue) {
        Objects.requireNonNull(map, "Map must not be null");
        Object value = map.get(key);
        if (value instanceof Number) {
            return ((Number) value).longValue();
        } else if (value instanceof String) {
            try {
                return Long.parseLong((String) value);
            } catch (NumberFormatException e) {
                return defaultValue;
            }
        }
        return defaultValue;
    }

    /**
     * 从 Map 中获取 Double 类型的值
     *
     * @param map          Map
     * @param key          键
     * @param defaultValue 默认值
     * @return 值或默认值
     */
    public static Double getDouble(Map<?, ?> map, Object key, Double defaultValue) {
        Objects.requireNonNull(map, "Map must not be null");
        Object value = map.get(key);
        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        } else if (value instanceof String) {
            try {
                return Double.parseDouble((String) value);
            } catch (NumberFormatException e) {
                return defaultValue;
            }
        }
        return defaultValue;
    }

    /**
     * 从 Map 中获取 Boolean 类型的值
     *
     * @param map          Map
     * @param key          键
     * @param defaultValue 默认值
     * @return 值或默认值
     */
    public static Boolean getBoolean(Map<?, ?> map, Object key, Boolean defaultValue) {
        Objects.requireNonNull(map, "Map must not be null");
        Object value = map.get(key);
        if (value instanceof Boolean) {
            return (Boolean) value;
        } else if (value instanceof String) {
            return Boolean.parseBoolean((String) value);
        }
        return defaultValue;
    }

    /**
     * 从 Map 中获取 BigDecimal 类型的值
     *
     * @param map          Map
     * @param key          键
     * @param defaultValue 默认值
     * @return 值或默认值
     */
    public static BigDecimal getBigDecimal(Map<?, ?> map, Object key, BigDecimal defaultValue) {
        Objects.requireNonNull(map, "Map must not be null");
        Object value = map.get(key);
        if (value instanceof BigDecimal) {
            return (BigDecimal) value;
        } else if (value instanceof Number) {
            return BigDecimal.valueOf(((Number) value).doubleValue());
        } else if (value instanceof String) {
            try {
                return new BigDecimal((String) value);
            } catch (NumberFormatException e) {
                return defaultValue;
            }
        }
        return defaultValue;
    }

    /**
     * 从 Map 中获取指定类型的值
     *
     * @param map          Map
     * @param key          键
     * @param clazz        目标类型
     * @param defaultValue 默认值
     * @param <T>          目标类型
     * @return 值或默认值
     */
    @SuppressWarnings("unchecked")
    public static <T> T getValue(Map<?, ?> map, Object key, Class<T> clazz, T defaultValue) {
        Objects.requireNonNull(map, "Map must not be null");
        Object value = map.get(key);
        if (clazz.isInstance(value)) {
            return (T) value;
        }
        return defaultValue;
    }

    /**
     * 从 Map 中获取嵌套的 Map
     *
     * @param map          Map
     * @param key          键
     * @param keyClass     嵌套 Map 的键类型
     * @param valueClass   嵌套 Map 的值类型
     * @param defaultValue 默认值
     * @param <K>          嵌套 Map 的键类型
     * @param <V>          嵌套 Map 的值类型
     * @return 嵌套的 Map 或默认值
     */
    public static <K, V> Map<K, V> getMap(Map<?, ?> map, Object key, Class<K> keyClass, Class<V> valueClass, Map<K, V> defaultValue) {
        Objects.requireNonNull(map, "Map must not be null");
        Object value = map.get(key);
        if (value instanceof Map<?, ?> rawMap) {
            Map<K, V> result = new HashMap<>();
            for (Map.Entry<?, ?> entry : rawMap.entrySet()) {
                K mapKey = convertValue(entry.getKey(), keyClass);
                V mapValue = convertValue(entry.getValue(), valueClass);
                if (mapKey != null && mapValue != null) {
                    result.put(mapKey, mapValue);
                }
            }
            return result;
        }
        return defaultValue;
    }

    /**
     * 从 Map 中获取 List
     *
     * @param map          Map
     * @param key          键
     * @param elementClass List 元素类型
     * @param defaultValue 默认值
     * @param <T>          List 元素类型
     * @return List 或默认值
     */
    public static <T> List<T> getList(Map<?, ?> map, Object key, Class<T> elementClass, List<T> defaultValue) {
        Objects.requireNonNull(map, "Map must not be null");
        Object value = map.get(key);
        if (value instanceof List<?> rawList) {
            List<T> result = new ArrayList<>();
            for (Object item : rawList) {
                T element = convertValue(item, elementClass);
                if (element != null) {
                    result.add(element);
                }
            }
            return result;
        }
        return defaultValue;
    }

    /**
     * 从 Map 中获取 Set
     *
     * @param map          Map
     * @param key          键
     * @param elementClass Set 元素类型
     * @param defaultValue 默认值
     * @param <T>          Set 元素类型
     * @return Set 或默认值
     */
    public static <T> Set<T> getSet(Map<?, ?> map, Object key, Class<T> elementClass, Set<T> defaultValue) {
        Objects.requireNonNull(map, "Map must not be null");
        Object value = map.get(key);
        if (value instanceof Set<?> rawSet) {
            Set<T> result = new HashSet<>();
            for (Object item : rawSet) {
                T element = convertValue(item, elementClass);
                if (element != null) {
                    result.add(element);
                }
            }
            return result;
        }
        return defaultValue;
    }

    /**
     * 从 Map 中获取自定义实体类
     *
     * @param map          Map
     * @param key          键
     * @param entityClass  实体类类型
     * @param defaultValue 默认值
     * @param <T>          实体类类型
     * @return 实体类实例或默认值
     */
    public static <T> T getEntity(Map<?, ?> map, Object key, Class<T> entityClass, T defaultValue) {
        Objects.requireNonNull(map, "Map must not be null");
        Object value = map.get(key);
        if (value instanceof Map) {
            return convertToEntity((Map<?, ?>) value, entityClass);
        }
        return defaultValue;
    }

    /**
     * 将 Map 转换为自定义实体类
     *
     * @param map         Map
     * @param entityClass 实体类类型
     * @param <T>         实体类类型
     * @return 实体类实例
     */
    private static <T> T convertToEntity(Map<?, ?> map, Class<T> entityClass) {
        try {
            Constructor<T> constructor = entityClass.getDeclaredConstructor();
            constructor.setAccessible(true);
            T entity = constructor.newInstance();
            for (Map.Entry<?, ?> entry : map.entrySet()) {
                if (entry.getKey() instanceof String fieldName) {
                    try {
                        java.lang.reflect.Field field = entityClass.getDeclaredField(fieldName);
                        field.setAccessible(true);
                        field.set(entity, convertValue(entry.getValue(), field.getType()));
                    } catch (NoSuchFieldException | IllegalAccessException e) {
                        // 忽略无法设置的字段
                    }
                }
            }
            return entity;
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException("Failed to create entity instance", e);
        }
    }

    /**
     * 将值转换为指定类型
     *
     * @param value 原始值
     * @param clazz 目标类型
     * @param <T>   目标类型
     * @return 转换后的值
     */
    @SuppressWarnings("unchecked")
    private static <T> T convertValue(Object value, Class<T> clazz) {
        if (value == null) {
            return null;
        }
        if (clazz.isInstance(value)) {
            return (T) value;
        }
        if (clazz == String.class) {
            return (T) value.toString();
        }
        if (clazz == Integer.class || clazz == int.class) {
            return (T) Integer.valueOf(value.toString());
        }
        if (clazz == Long.class || clazz == long.class) {
            return (T) Long.valueOf(value.toString());
        }
        if (clazz == Double.class || clazz == double.class) {
            return (T) Double.valueOf(value.toString());
        }
        if (clazz == Boolean.class || clazz == boolean.class) {
            return (T) Boolean.valueOf(value.toString());
        }
        if (clazz == BigDecimal.class) {
            return (T) new BigDecimal(value.toString());
        }
        throw new IllegalArgumentException("Unsupported type: " + clazz.getName());
    }

    /**
     * 将自定义实体类转换为 Map
     *
     * @param entity 实体类实例
     * @return 转换后的 Map
     */
    public static Map<String, Object> toMap(Object entity) {
        Objects.requireNonNull(entity, "Entity must not be null");
        Map<String, Object> result = new HashMap<>();
        Class<?> clazz = entity.getClass();

        // 遍历实体类的所有字段
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true); // 允许访问私有字段
            try {
                Object value = field.get(entity);
                result.put(field.getName(), value);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Failed to access field: " + field.getName(), e);
            }
        }

        return result;
    }

    /**
     * 将自定义实体类转换为 Map，并支持字段名的自定义映射
     *
     * @param entity        实体类实例
     * @param fieldNameMapper 字段名映射函数
     * @return 转换后的 Map
     */
    public static Map<String, Object> toMap(Object entity, Function<String, String> fieldNameMapper) {
        Objects.requireNonNull(entity, "Entity must not be null");
        Objects.requireNonNull(fieldNameMapper, "Field name mapper must not be null");
        Map<String, Object> result = new HashMap<>();
        Class<?> clazz = entity.getClass();

        // 遍历实体类的所有字段
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true); // 允许访问私有字段
            try {
                Object value = field.get(entity);
                String fieldName = fieldNameMapper.apply(field.getName());
                result.put(fieldName, value);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Failed to access field: " + field.getName(), e);
            }
        }

        return result;
    }
}
